﻿using System;
using System.IO;
using System.Reflection;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;

namespace SharePointPOBox.PostOffice
{
    public partial class Box : Page
    {
        #region properties

        /// <summary>Id of item related to download file</summary>
        private int ManageItemID
        {
            get
            {
                if (this.ViewState["TargetId"] == null)
                {
                    return -1;
                }
                else
                {
                    return (int)this.ViewState["TargetId"];
                }
            }
            set
            {
                this.ViewState["TargetId"] = value;
            }
        }

        private SPListItem _ManageListItem = null;
        /// <summary>List item related to download file</summary>
        private SPListItem ManageListItem
        {
            get
            {
                if (_ManageListItem == null)
                {
                    _ManageListItem
                        = Common.ManageListBySystemAccount.GetItemByIdAllFields(ManageItemID);
                }
                return _ManageListItem;
            }
        }
        
        #endregion properties

        #region events

        /// <summary>Page.Load</summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void Page_Load(object sender, EventArgs e)
        {
            // Get item related to download file.
            SPQuery qry = new SPQuery()
            {
                Query = "<Where>"
                        + "<Eq>"
                            + "<FieldRef Name='DownloadKey'/>"
                            + "<Value Type='Text'>" 
                                + Request.QueryString["uk"] 
                            + "</Value>"
                        +"</Eq>"
                        +"</Where>",
                RowLimit = 1,
                ViewFields = "<FieldRef Name='ID'/><FieldRef Name='DownloadDeadline'/>"
            };
            SPListItemCollection itmRet = Common.ManageListBySystemAccount.GetItems(qry);

            if (itmRet.Count != 1)
            {
                // Get item failed..
                throw new SPException(Common.CurLngStr("SPPOBWrongAccess"));
            }

            // Check download deadline
            if ((DateTime)(itmRet[0][itmRet.Fields.GetFieldByInternalName("DownloadDeadline").Id] ?? DateTime.MinValue) < DateTime.Today)
            {
                // download deadline over
                throw new SPException(Common.CurLngStr("SPPOBLimitOver"));
            }
            
            ManageItemID = itmRet[0].ID;

            btnDownload.ServerClick += new EventHandler(execDownload);
            PasswordValidator.ServerValidate += new ServerValidateEventHandler(PasswordValidate);
        }

        /// <summary>Page.PreRender</summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void Page_PreRender(object sender, EventArgs e)
        {
            // Check download count
            int iDownloadLimit = (int)(ManageListItem[ManageListItem.Fields.GetFieldByInternalName("DownloadLimit").Id] ?? 0);
            if(iDownloadLimit < 1)
            {
                // Download limit over
                lbDlRemainNum.InnerText = Common.CurLngStr("SPPOBLimitCountOver");
                btnDownload.Visible
                    = pwdPassword.Visible
                    = lbPassword.Visible
                    = false;
                foreach(Control ctlVal in this.Validators )
                {
                    ctlVal.Visible = false;
                }
            }
            else
            {
                lbDlRemainNum.InnerText
                    = Common.CurLngStr("SPPOBLimitCountLabelPrefix")
                    + iDownloadLimit.ToString() + Common.CurLngStr("SPPOBTimesPrefix");
            }
        }

        /// <summary>Validate input password.</summary>
        /// <param name="source"></param>
        /// <param name="args"></param>
        protected void PasswordValidate(object source, ServerValidateEventArgs args)
        {
            args.IsValid
                = (string)(ManageListItem[ManageListItem.Fields.GetFieldByInternalName("Password").Id] ?? string.Empty)
                    == pwdPassword.Value;
        }

        /// <summary>Click event of download button.</summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void execDownload(object sender, EventArgs e)
        {
            this.Validate();
            if (this.IsValid)
            {
                // Check download deadline
                if ((DateTime)(ManageListItem[ManageListItem.Fields.GetFieldByInternalName("DownloadDeadline").Id] ?? DateTime.MinValue) <= DateTime.Today)
                {
                    throw new SPException(Common.CurLngStr("SPPOBLimitOver"));
                }
                // Check download count
                if ((int)(ManageListItem[ManageListItem.Fields.GetFieldByInternalName("DownloadLimit").Id] ?? 0) <= 0)
                {
                    throw new SPException(Common.CurLngStr("SPPOBLimitCountOver"));
                }

                // Get document id from query string
                string sDocId = string.Empty;
                SPFieldUrlValue fuv
                    = new SPFieldUrlValue((string)ManageListItem[ManageListItem.Fields.GetFieldByInternalName("FileUrl").Id]);
                string sDocUrlQueryStrings = fuv.Url.Split("?".ToCharArray(), 2)[1];
                foreach (string sQueryParam in sDocUrlQueryStrings.Split("&".ToCharArray()))
                {
                    string[] sParamValue = sQueryParam.Split("=".ToCharArray());
                    if (sParamValue[0].ToLower() == "id")
                    {
                        sDocId = sParamValue[1];
                        break;
                    }
                }

                // Get file to download
                SPFile file = null;
                SPSecurity.RunWithElevatedPrivileges(delegate()
                {
                    string sDocUrl = string.Empty;
                    using (SPSite site = new SPSite(SPContext.Current.Site.ID))
                    {
                        Assembly asmDocManagement = Assembly.Load("Microsoft.Office.DocumentManagement, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c");
                        Type clsDocIdLookup = asmDocManagement.GetType("Microsoft.Office.DocumentManagement.DocumentId");
                        MethodInfo miFindUrlsById = clsDocIdLookup.GetMethod("FindUrlsById", BindingFlags.Public | BindingFlags.Static);
                        string[] sResults = (string[])miFindUrlsById.Invoke(null, new object[] { site, sDocId });

                        if (sResults == null || sResults.Length < 1)
                        {
                            throw new SPException(Common.CurLngStr("SPPOBWrongAccess"));
                        }
                        sDocUrl = sResults[0];
                    }
                    // Add sDocUrl to HttpContext item for audit log
                    System.Web.HttpContext.Current.Items["DocumentPath"] = sDocUrl;

                    using (SPSite site = new SPSite(sDocUrl))
                    {
                        using (SPWeb web = site.OpenWeb())
                        {
                            file = web.GetFile(sDocUrl);
                        }
                    }

                    // output
                    Response.ContentType = "application/octet-stream";
                    Response.AppendHeader("X-Content-Type-Options", "nosniff");
                    Response.AppendHeader("X-Download-Options", "noopen");
                    Response.AddHeader("Content-Disposition"
                        , "attachment; filename=" + SPHttpUtility.UrlEncodeFilenameForHttpHeader(ManageListItem.Title ?? "NoNameFile"));

                    byte[] bin = new byte[256];
                    Stream outstm = Response.OutputStream;
                    Stream instm = file.OpenBinaryStream();
                    try
                    {
                        int iReadLen;
                        do
                        {
                            iReadLen = instm.Read(bin, 0, bin.Length);
                            if (iReadLen == 0)
                            {
                                return;
                            }
                            outstm.Write(bin, 0, iReadLen);
                        }
                        while (iReadLen >= bin.Length);
                    }
                    catch
                    {
                        Response.ClearHeaders();
                        Response.ClearContent();
                        throw;
                    }
                    finally
                    {
                        Response.Flush();

                        int iDlLimit = (int)ManageListItem[ManageListItem.Fields.GetFieldByInternalName("DownloadLimit").Id];
                        ManageListItem[ManageListItem.Fields.GetFieldByInternalName("DownloadLimit").Id] = iDlLimit - 1;
                        ManageListItem.Web.AllowUnsafeUpdates = true;
                        ManageListItem.SystemUpdate();

                        // Write audit log
                        Common.WriteAuditLog(ManageListItem);
                    }
                });
                try
                {
                    Response.End();
                }
                catch (System.Threading.ThreadAbortException taex)
                {
                    // normal route.
                }
            }
        }

        #endregion events
    }
}
